﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

/*
 * 公钥与密钥
 */
namespace SimpleServer.Net
{
    public class ServerSocket : Singleton<ServerSocket>
    {
        //公钥
        public static string PublicKey = "abc123";
        //密钥
        public static string SecretKey = "simpleServer";

        private static Socket m_ListenSocket;
#if DEBUG
        private string m_IpStr = "127.0.0.1";//本地地址
#else
        private string m_IpStr="78.89.56.46";//云服务器地址
#endif
        private const int m_Port = 8011;//端口号
        private static List<Socket> m_CheckReadList = new List<Socket>();//存储所有socket的集合
        private static Dictionary<Socket, ClientSocket> m_clientDic = new Dictionary<Socket, ClientSocket>();//存储所有客户端的数据
        private int m_PingInterval = 30;//心跳包时间间隔
        private List<ClientSocket> m_TempList = new List<ClientSocket>();//存储心跳包超时的socket
        public void Init()
        {
            IPAddress ip = IPAddress.Parse(m_IpStr);
            IPEndPoint iPEndPoint = new IPEndPoint(ip, m_Port);
            m_ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_ListenSocket.Bind(iPEndPoint);
            m_ListenSocket.Listen(1000);
            Debug.LogInfo("服务器启动监听{0}成功", m_ListenSocket.LocalEndPoint.ToString());
            while (true)
            {
                ResetCheckRead();
                try
                {
                    Socket.Select(m_CheckReadList, null, null, 1000);
                }
                catch (Exception e)
                {
                    Debug.LogError(e);
                }
                for (int i = m_CheckReadList.Count - 1; i >= 0; i--)
                {
                    Socket socket = m_CheckReadList[i];
                    if (socket == m_ListenSocket)
                    {
                        //有客户端向服务端发起连接请求
                        ReadListen(socket);
                    }
                    else
                    {
                        //说明连接的客户端中有数据发送
                        ReadClient(socket);
                    }
                }
                //统一检查所有客户端Socket的心跳包是否超时
                long timeNow = GetTimeStamp();
                // 用一个List临时存储当前所有心跳包超时的Socket
                foreach (ClientSocket client in m_clientDic.Values)
                {
                    if (timeNow - client.LastPingTime > m_PingInterval * 4)
                    {
                        Debug.Log("心跳包超时！" + client.Socket.RemoteEndPoint.ToString());
                        m_TempList.Add(client);
                    }
                }
                // 将所有超时的客户端Socket断开连接
                foreach (ClientSocket clientSocket in m_TempList)
                {
                    CloseClient(clientSocket);
                }
                m_TempList.Clear();
            }
        }
        //检测哪些socket是可读取的
        public void ResetCheckRead()
        {
            m_CheckReadList.Clear();
            m_CheckReadList.Add(m_ListenSocket);
            foreach (Socket s in m_clientDic.Keys)
            {
                m_CheckReadList.Add(s);
            }
        }
        //处理客户端连接
        void ReadListen(Socket listen)
        {
            try
            {
                Socket client = listen.Accept();
                //把连接到服务器的客户端给存起来
                ClientSocket clientSocket = new ClientSocket();
                clientSocket.Socket = client;
                clientSocket.LastPingTime = GetTimeStamp();
                m_clientDic.Add(client, clientSocket);
                Debug.Log("一个客户端连接：{0}，当前有{1}个客户端在线！", client.LocalEndPoint.ToString(), m_clientDic.Count);
            }
            catch (SocketException e)
            {
                Debug.LogError("连接失败！" + e.ToString());
            }
        }
        //处理客户端的数据
        void ReadClient(Socket client)
        {
            ClientSocket clientSocket = m_clientDic[client];
            ByteArray readBuff = clientSocket.ReadBuff;//获取对应的客户端的缓冲区数据
            int count = 0;
            if (readBuff.Reamin <= 0)
            {
                OnReceiveData(clientSocket);
                readBuff.CheckAndMoveBytes();
                while (readBuff.Reamin <= 0)
                {
                    int expandSize = readBuff.Length < ByteArray.DEFAULT_SIZE ? ByteArray.DEFAULT_SIZE : readBuff.Length;
                    readBuff.ReSize(expandSize * 2);
                }
            }
            //接收消息并处理
            try
            {
                count = client.Receive(readBuff.Bytes, readBuff.WriteIdx, readBuff.Reamin, 0);
            }
            catch (SocketException e)
            {
                Debug.LogError("接收消息异常！" + e.ToString());
                CloseClient(clientSocket);
                return;
            }
            if (count <= 0)
            {
                CloseClient(clientSocket);
                return;
            }
            readBuff.WriteIdx += count;
            //对接收到的消息进行解析
            OnReceiveData(clientSocket);
            //进行数据移动
            readBuff.CheckAndMoveBytes();
        }
        //对接收到的消息进行处理
        void OnReceiveData(ClientSocket clientSocket)
        {
            ByteArray readBuff = clientSocket.ReadBuff;
            if (readBuff.Length <= 4 || readBuff.ReadIdx < 0)
            {
                return;
            }
            int readIdx = readBuff.ReadIdx;
            byte[] bytes = readBuff.Bytes;
            int bodyLength = BitConverter.ToInt32(bytes, readIdx);//消息包(名字+内容)的长度
            if (readBuff.Length < bodyLength + 4)//处理的就是分包
            {
                return;
            }
            //对完整的消息进行解析处理
            readBuff.ReadIdx += 4;
            //解析协议名
            int nameCount = 0;
            ProtocolEnum protocol = ProtocolEnum.None;
            try
            {
                protocol = MsgBase.DecodeName(readBuff.Bytes, readBuff.ReadIdx, out nameCount);
            }
            catch (Exception e)
            {
                Debug.LogError("解析协议名异常！" + e.ToString());
                CloseClient(clientSocket);
                return;
            }
            if (protocol == ProtocolEnum.None)
            {
                Debug.LogError("解析协议名DecodeName失败！");
                CloseClient(clientSocket);
                return;
            }
            //解析协议内容
            readBuff.ReadIdx += nameCount;
            int bodyCount = bodyLength - nameCount;
            MsgBase msgBase = null;
            try
            {
                msgBase = MsgBase.Decode(protocol, readBuff.Bytes, readBuff.ReadIdx, bodyCount);
            }
            catch (Exception e)
            {
                Debug.LogError("解析协议内容异常！" + e.ToString());
                CloseClient(clientSocket);
                return;
            }
            if (msgBase == null)
            {
                Debug.LogError("解析协议名Decode失败！" + protocol.ToString());
                CloseClient(clientSocket);
                return;
            }
            readBuff.ReadIdx += bodyCount;
            readBuff.CheckAndMoveBytes();
            //以上是一条完整的消息解析完成
            //对解析完成的数据进行处理
            //通过反射进行数据处理，消息分发
            MethodInfo methodInfo = typeof(MsgHandler).GetMethod(protocol.ToString());
            object[] obj = { clientSocket, msgBase };
            if (methodInfo != null)
            {
                methodInfo.Invoke(null, obj);
            }
            else
            {
                Debug.LogError("未找到该协议与其相对应的方法：" + protocol.ToString());
            }
            //粘包
            if (readBuff.Length > 4)
            {
                OnReceiveData(clientSocket);
            }
        }
        //对处理完成后的消息需要进行下发  发送数据到客户端
        public static void Send(ClientSocket clientSocket, MsgBase msgBase)
        {
            if (clientSocket == null || !clientSocket.Socket.Connected)
            {
                return;
            }
            try
            {
                byte[] nameBytes = MsgBase.EncodeName(msgBase);
                byte[] bodyBytes = MsgBase.Encode(msgBase);
                int len = nameBytes.Length + bodyBytes.Length;
                byte[] headBytes = BitConverter.GetBytes(len);
                byte[] sendBytes = new byte[len + headBytes.Length];
                Array.Copy(headBytes, 0, sendBytes, 0, headBytes.Length);
                Array.Copy(nameBytes, 0, sendBytes, headBytes.Length, nameBytes.Length);
                Array.Copy(bodyBytes, 0, sendBytes, headBytes.Length + nameBytes.Length, bodyBytes.Length);
                try
                {
                    clientSocket.Socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, null, null);
                }
                catch (SocketException e)
                {
                    Debug.LogError("socket beginSend异常！" + e.ToString());
                    throw;
                }
            }
            catch (SocketException e)
            {
                Debug.LogError("socket发送数据异常！" + e.ToString());
            }
        }
        //关闭socket 
        public void CloseClient(ClientSocket client)
        {
            client.Socket.Close();
            m_clientDic.Remove(client.Socket);
            Debug.Log("一个客户端断开连接，当前总连接数：{0}", m_clientDic.Count);
        }

        //获取当前时间戳
        public static long GetTimeStamp()
        {
            TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(timeSpan.TotalSeconds);
        }
    }
}
